package com.eq.service;

import com.eq.dto.FileItem;

import jakarta.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.util.FileSystemUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.stream.Stream;

@Service
public class FileStorageServiceImpl implements FileStorageService {

    @Value("${file.upload-dir}")
    private String uploadDir;

    private Path rootLocation;

    @Override
    @PostConstruct
    public void init() {
        try {
            rootLocation = Paths.get(uploadDir);
            Files.createDirectories(rootLocation);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void store(MultipartFile file) {
        try {
            if (file.isEmpty()) {
                throw new RuntimeException("Failed to store empty file.");
            }
            Path destinationFile = this.rootLocation.resolve(
                    Paths.get(file.getOriginalFilename()))
                    .normalize();
            if (!destinationFile.getParent().equals(this.rootLocation)) {
                throw new RuntimeException("Cannot store file outside current directory.");
            }
            try (InputStream inputStream = file.getInputStream()) {
                Files.copy(inputStream, destinationFile,
                        StandardCopyOption.REPLACE_EXISTING);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Stream<Path> loadAll(String path) {
        try {
            Path fullPath = rootLocation.resolve(path);
            return Files.walk(fullPath, 1)
                    .filter(file_path -> !file_path.equals(fullPath));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void createDirectory(String path) {
        try {
            Path dirPath = this.rootLocation.resolve(path);
            Files.createDirectories(dirPath);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void delete(String path) {
        try {
            Path filePath = this.rootLocation.resolve(path);
            FileSystemUtils.deleteRecursively(filePath);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Resource loadAsResource(String filePath) {
        try {
            Path file = this.rootLocation.resolve(filePath);
            Resource resource = new UrlResource(file.toUri());
            if (resource.exists() || resource.isReadable()) {
                return resource;
            } else {
                throw new RuntimeException("Could not read file: " + filePath);
            }
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void deleteAll() {
        FileSystemUtils.deleteRecursively(rootLocation.toFile());
    }

    @Override
    public FileItem getFileMetadata(Path filePath) throws IOException {
        boolean isDirectory = Files.isDirectory(filePath);
        long size = isDirectory ? 0 : Files.size(filePath);
        return new FileItem(
                this.rootLocation.relativize(filePath).toString().replace("\\", "/"),
                filePath.getFileName().toString(),
                size,
                Files.getLastModifiedTime(filePath).toMillis(),
                isDirectory);
    }

    @Override
    public void rename(String sourcePath, String targetPath) {
        try {
            Path source = this.rootLocation.resolve(sourcePath);
            Path target = this.rootLocation.resolve(targetPath);

            Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
